#!/usr/bin/python
#
# Copyright 2012 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This code is not supported by Google
#
"""
GSA onebox server that returns triggered Google CommerceSearch Redirect and
Promotion rules.  This script is not intended for production use.

to use:
apt-get update
apt-get install python-cherrypy3 python-libxml2
--> specify your cx and API key
--> start the script  ./gcsobox.py --debug  --port=28080
--> on your GSA, define a onebox URL http://<oboxserver>:28080/
    * (remember the trailing '/')
    * specify to trigger on all queries (or if you want, on predefined queries that match
      your existing rules)
--> OBOX results returned in the GSA search response

<ENTOBRESULTS>
<OBRES module_name="gcs_onebox_module">
<description/>
<resultCode>success</resultCode>
<provider>GCS Onebox Results</provider>
<title>
<urlText>The query to onebox: widget</urlText>
<urlLink>
https://www.googleapis.com/shopping/search/v1/cx:your_cx/products?&alt=atom&q=widget&country=us&key=KEY
   &redirects.enabled=true&redirects.useGcsConfig=true&promotions.enabled=true&promotions.useGcsConfig=true
</urlLink>
</title>
<MODULE_RESULT>
<Title>Promotion</Title>
<Field name="type">standard</Field>
<Field name="name">Super Widget</Field>
<Field name="description">you must by this widget</Field>
<Field name="destLink">
http://www.googlestore.com/widget.html
</Field>
</MODULE_RESULT>
</OBRES>
</ENTOBRESULTS>
"""


import sys
import getopt
import datetime
import time
import base64
import sys
import logging
import urllib,urllib2
from urllib2 import URLError, HTTPError
import json
import httplib
import ssl, socket
import libxml2
import xml.dom.minidom
import cherrypy
from xml.sax.saxutils import escape
from urllib import quote_plus
from base64 import b64decode

class gcsobox(object):
  
  def index(self,**args):
    indexHTML = ('<html><title>GCS Onebox</title>'
                 '<body><center>'
                 '<h3>Landing page for GCS Onebox</h3>'
                 '<p>'
                 '<br>'
                 '</p><p>')
    q = None
    try:
      q = cherrypy.request.params['query']
    except KeyError:
      cherrypy.response.status = 500
      return
 
    if q!=None:
      q=quote_plus(q.encode('utf8'))
      indexHTML = self.__getGCSResponse(q)    
    return indexHTML
  index.exposed = True

  def default(self, attr='abc'):
    cherrypy.response.status = 404
    return "Page not Found"
  default.exposed = True
    
  def __getGCSResponse(self,query):
 
    cx = ''
    key = ''
    country = 'us' 
 
    promotions = []
    redirects = []
    baseURL= ('https://www.googleapis.com/shopping/search/v1/cx:' + cx + 
              '/products?&alt=atom&q=' + query + '&country='+ country + 
              '&key=' + key + '&redirects.enabled=true&redirects.useGcsConfig=true' +
              '&promotions.enabled=true&promotions.useGcsConfig=true')
    self.log(baseURL, logging.INFO)
    req = urllib2.Request(baseURL)
    try:
      response = urllib2.urlopen(req)
      content = response.read()  

      doc = libxml2.parseDoc(content)
      ctxt = doc.xpathNewContext()
      ctxt.xpathRegisterNs('openSearch', "http://a9.com/-/spec/opensearchrss/1.0/")
      ctxt.xpathRegisterNs('s', "http://www.google.com/shopping/api/schemas/2010")  
      ctxt.xpathRegisterNs('atom', "http://www.w3.org/2005/Atom")  
      ctxt.xpathRegisterNs('gd', "http://schemas.google.com/g/2005")
      nl_promotions =  ctxt.xpathEval('/atom:feed/s:promotions/s:promotion')
      nl_redirects =  ctxt.xpathEval('/atom:feed/s:redirects/s:redirect')
      for n_redirect in nl_redirects:
        ctxt.setContextNode(n_redirect)
        link = n_redirect.prop('link')
        redirects.append(('link',escape(link)))

      for n_promotion in nl_promotions:
        promo = []
        ctxt.setContextNode(n_promotion)
        ptype = n_promotion.prop('type')
        promo.append(('type',ptype))
        if ptype == 'custom':
          customHTML = n_promotion.prop('customHtml')
          promo.append(('customHtml',customHTML))
          nl_customs =  ctxt.xpathEval('s:customFields/s:customField')
          for n_custom in nl_customs:
            cname = n_custom.prop('name')
            cvalue = n_custom.prop('value')
            promo.append((escape('custom_' + cname),escape(cvalue)))
        if ptype == 'standard':         
          sname = n_promotion.prop('name')
          sdescription = n_promotion.prop('description')
          sdestLink = n_promotion.prop('destLink')
          simageLink = n_promotion.prop('imageLink')
          print sdescription
          promo.append(('name',escape(sname)))
          promo.append(('description',escape(sdescription)))
          promo.append(('destLink',escape(sdestLink)))
          if (simageLink != None):
            promo.append(('imageLink',escape(simageLink)))                            
          nl_customs =  ctxt.xpathEval('s:customFields/s:customField')
          for n_custom in nl_customs:
            cname = n_custom.prop('name')
            cvalue = n_custom.prop('value') 
            promo.append((escape('custom_' + cname),escape(cvalue)))
        if ptype == 'product': 
          provided_id =  ctxt.xpathEval('s:product/s:providedId')[0].content
          title =  ctxt.xpathEval('s:product/s:title')[0].content
          description =  ctxt.xpathEval('s:product/s:description')[0].content
          link =  ctxt.xpathEval('s:product/s:link')[0].content
          promo.append(('providedId',provided_id))
          promo.append(('title',escape(title)))
          promo.append(('description',escape(description)))
          promo.append(('link',escape(link)))
          nl_attributes = ctxt.xpathEval('s:product/s:attributes/s:attribute')
          for n_attribute in nl_attributes:
            ctxt.setContextNode(n_attribute)
            aname = n_attribute.prop('name')
            avalue = ctxt.xpathEval('.')[0].content
            promo.append((escape(aname),escape(avalue)))
          for n_custom in nl_customs:
            cname = n_custom.prop('name')
            cvalue = n_custom.prop('value') 
            promo.append((escape('custom_' + cname),escape(cvalue)))            
            
        promotions.append(promo)                

      doc.freeDoc()
      ctxt.xpathFreeContext()
      
      header = """<OneBoxResults>
                  <resultCode>success</resultCode>
                  <provider>GCS Onebox Results</provider>
                  <title>
                    <urlText>The query to onebox:  %s</urlText>\n
                    <urlLink>%s</urlLink>\n
                  </title>\n""" %(escape(query),escape(baseURL))
      footer = "</OneBoxResults>\n"
      
      p = ''
      for promo in promotions:
        out = '<MODULE_RESULT>\n'
        out = out + '<Title>Promotion</Title>\n'
        for a in promo:
          out = out + '<Field name="' + a[0] + '">' + a[1] + '</Field>\n'
        out = out + '</MODULE_RESULT>\n'
        p = p + out
      
      q = ''
      for rdr in redirects:
        out = '<MODULE_RESULT>\n'
        out = out + '<Title>Redirect</Title>\n'     
        out = out + '<Field name="' + rdr[0] + '">' + rdr[1] + '</Field>\n'
        out = out + '</MODULE_RESULT>\n'
        q = q + out    
        
      return (header + p + q + footer).strip()
   
    except urllib2.HTTPError, e:
      self.log('HTTPERROR: .',logging.ERROR)
      self.log('Error code: ' + str(e),logging.ERROR)
    except urllib2.URLError, e:
      self.log('URLERROR .',logging.ERROR)
      self.log( 'Reason: ' + str(e.reason ),logging.ERROR)
    return None
        
  def log(self,msg, loglevel):
    #LOG_FILENAME = 'gcsoauth.log'
    #logging.basicConfig(filename=LOG_FILENAME,level=logging.INFO)
    m = ('[%s] %s') % (datetime.datetime.now(), msg)
    print m
    if (loglevel == logging.DEBUG):
      logging.debug(m)   
    else:
      logging.info(m)
      
def main():
  # Default listen port
  cherrypy.server.socket_port = 28080
  cherrypy.server.socket_host =  '0.0.0.0'


  try:
    opts, args = getopt.getopt(sys.argv[1:], None,
                               ["debug","port=", "use_ssl"])
  except getopt.GetoptError:
    sys.exit(1)

  cherrypy.config.update({'global':{'log.screen': False}})
  for opt, arg in opts:
    if opt == "--debug":
      cherrypy.config.update({'global':{'log.screen': True}})
    if opt == "--use_ssl":
      cherrypy.config.update({"global": {
          "server.ssl_certificate": "ssl.crt",
          "server.ssl_private_key": "ssl.key"}})
    if opt == "--port":
      port = int(arg)
      cherrypy.config.update({"global": {"server.socket_port": port}})

    cherrypy.config.update({"global": {
          'checker.on': False,
          'tools.log_headers.on': False,
          'request.show_tracebacks': False,
          'request.show_mismatched_params': False,
          'log.screen': True}})
  cherrypy.quickstart(gcsobox())      
      

if __name__ == '__main__':
  main()